from typing import List, Dict, Any, Optional, BinaryIO, Union

class VFSClient:
    def __init__(self, vfs_core: 'VirtualFileSystem'):
        self.vfs = vfs_core

    # ==============================
    # 文件上传：秒传 vs 普通上传（分离）
    # ==============================

    def check_quick_upload(self, file_md5: str) -> bool:
        """
        检测是否支持秒传（根据文件 MD5 判断系统中是否存在相同内容）
        """
        return self.vfs.check_quick_upload(file_md5)

    def quick_upload(
        self,
        file_md5: str,
        vfs_path: str,
        overwrite: bool = True,
        **meta
    ) -> int:
        """
        秒传：不上传文件内容，仅创建虚拟文件引用（前提是系统已存在该 MD5 内容）
        - file_md5: 文件内容的 MD5（32位小写）
        - vfs_path: 虚拟目标路径，如 "/user/docs/report.pdf"
        - overwrite: 是否覆盖现有文件，默认为True（WebDAV PUT语义）
        - meta: 附加元数据
        返回 0 表示成功，失败则抛出异常（如 FileNotFoundError 表示内容不存在）
        """
        return self.vfs.quick_upload(file_md5, vfs_path, overwrite, **meta)

    def upload_file(
        self,
        file_stream: Union[bytes, BinaryIO],
        vfs_path: str,
        overwrite: bool = True,
        **meta
    ) -> int:
        """
        普通上传：传入完整文件字节流，VFS 负责存储并创建虚拟文件
        - file_stream: 文件内容（bytes 或可读 BinaryIO）
        - vfs_path: 虚拟目标路径
        - overwrite: 是否覆盖现有文件，默认为True（WebDAV PUT语义）
        - meta: 附加元数据
        返回 0 表示成功
        """
        return self.vfs.upload_file(file_stream, vfs_path, overwrite, **meta)
    
    def upload_file_stream(
        self,
        file_stream: BinaryIO,
        vfs_path: str,
        overwrite: bool = True,
        **meta
    ) -> int:
        """
        流式上传：支持流式文件上传，适用于大文件和WebDAV协议
        - file_stream: 可读的文件流对象
        - vfs_path: 虚拟目标路径
        - overwrite: 是否覆盖现有文件，默认为True（WebDAV PUT语义）
        - meta: 附加元数据
        返回 0 表示成功
        """
        return self.vfs.upload_file_stream(file_stream, vfs_path, overwrite, **meta)

    # ==============================
    # 文件下载
    # ==============================

    def get_download_url(self, vfs_path: str, expires_in: int = 3600) -> str:
        """
        获取临时下载链接（用于 302 重定向或直链）
        
        这是VFS提供的唯一文件下载接口，调用方需要根据返回的URL自行实现下载逻辑。
        例如：
        - 测试脚本可以使用requests.get(url)下载文件
        - WebDAV可以使用302重定向到该URL
        """
        return self.vfs.get_download_url(vfs_path, expires_in=expires_in)

    # ==============================
    # 基础文件系统操作
    # ==============================

    def mkdir(self, path: str) -> int:
        return self.vfs.mkdir(path)

    def ls(self, path: str) -> List[Dict[str, Any]]:
        nodes = self.vfs.list_dir(path)
        return [self._serialize_node(n) for n in nodes]

    def stat(self, path: str) -> Dict[str, Any]:
        node = self.vfs.get_info(path)
        return self._serialize_node(node)

    def exists(self, path: str) -> bool:
        # 根目录总是存在
        if path == "/" or path == "":
            return True
        return self.vfs._resolve_path(path) is not None

    def is_dir(self, path: str) -> bool:
        # 根目录总是目录
        if path == "/" or path == "":
            return True
        try:
            node = self.vfs.get_info(path)
            return node.is_dir
        except FileNotFoundError:
            return False

    # ==============================
    # 文件/目录变更
    # ==============================

    def mv(self, src: str, dst: str, overwrite: bool = False) -> bool:
        return self.vfs.move(src, dst, overwrite=overwrite)

    def cp(self, src: str, dst: str, overwrite: bool = False) -> int:
        return self.vfs.copy(src, dst, overwrite=overwrite)

    def rename(self, path: str, new_name: str) -> bool:
        return self.vfs.rename(path, new_name)

    def rm(self, path: str, recursive: bool = False) -> bool:
        return self.vfs.delete(path, force=False, recursive=recursive)

    def rm_force(self, path: str, recursive: bool = False) -> bool:
        return self.vfs.delete(path, force=True, recursive=recursive)

    # ==============================
    # 回收站操作（基于 original_path）
    # ==============================

    def trash_list(self) -> List[Dict[str, Any]]:
        nodes = self.vfs.list_trash()
        return [self._serialize_node(n) for n in nodes]

    def trash_restore(self, original_path: str, target_path: Optional[str] = None) -> bool:
        return self.vfs.restore_from_trash_by_original_path(original_path, target_path)

    def trash_delete(self, original_path: str) -> bool:
        return self.vfs.delete_from_trash_by_original_path(original_path)

    def trash_empty(self) -> int:
        return self.vfs.empty_trash()

    def get_trash_info(self, original_path: str) -> Dict[str, Any]:
        node = self.vfs.get_trash_item_by_original_path(original_path)
        return self._serialize_node(node)

    # ==============================
    # 辅助方法
    # ==============================

    def _serialize_node(self, node) -> Dict[str, Any]:
        d = {
            'name': node.name,
            'is_dir': node.is_dir,
            'created_at': node.created_at,
            'updated_at': node.updated_at,
            'meta': dict(node.meta) if node.meta else {},
        }
        if not node.is_dir:
            d['size'] = getattr(node, 'size', 0)
            d['md5'] = getattr(node, 'file_md5', None)
        if hasattr(node, 'original_path') and node.original_path is not None:
            d['original_path'] = node.original_path
            d['deleted_at'] = node.deleted_at
        return d